﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using Common;
using Unity.VisualScripting;
using UnityEditor;

namespace Event
{
    internal sealed partial class EventPool<T> where T : BaseEventArgs
    {
        /// <summary>
        /// 事件处理程序字典。key为事件ID，value为事件处理程序列表
        /// </summary>
        private readonly Dictionary<int, List<EventHandler<T>>> m_EventHandlers;

        /// <summary>
        /// 待处理的事件，下一帧处理
        /// </summary>
        private readonly Queue<Event> m_Events;

        /// <summary>
        /// 缓存节点字典，用于在遍历过程过程中缓存链表节点
        /// </summary>
        private readonly Dictionary<object, LinkedListNode<EventHandler<T>>> m_CachedNodes;
        
        /// <summary>
        /// 临时节点字典，辅助缓存节点操作
        /// </summary>
        private readonly Dictionary<object, LinkedListNode<EventHandler<T>>> m_TempNodes;

        /// <summary>
        /// 事件池模式
        /// </summary>
        private readonly EventPoolMode m_EventPoolMode;
        /// <summary>
        /// 默认处理函数
        /// </summary>
        private EventHandler<T> m_DefaultHandler;

        public EventPool(EventPoolMode mode)
        {
            m_EventHandlers = new();
            m_Events = new Queue<Event>();
            m_CachedNodes = new ();
            m_TempNodes = new ();
            m_EventPoolMode = mode;
            m_DefaultHandler = null;
        }

        /// <summary>
        /// 获取事件处理函数数量
        /// </summary>
        public int EventHandlerCount
        {
            get { return m_EventHandlers.Count; }
        }


        /// <summary>
        /// 获取下一帧需要执行的事件数量
        /// </summary>
        public int EventCount
        {
            get { return m_Events.Count; }
        }

        /// <summary>
        /// 事件池轮询
        /// </summary>
        /// <param name="elapseSeconds">逻辑流逝时间</param>
        /// <param name="realElapseSeconds">真实流逝时间</param>
        public void Update(float elapseSeconds, float realElapseSeconds)
        {
            lock (m_Events)
            {
                while (m_Events.Count > 0)
                {
                    Event eventNode = m_Events.Dequeue();
                    ReferencePool.Release(eventNode);
                }
            }
        }

        /// <summary>
        /// 关闭并清理事件池
        /// </summary>
        public void Shutdown()
        {
            Clear();
            m_EventHandlers.Clear();
            m_CachedNodes.Clear();
            m_TempNodes.Clear();
            m_DefaultHandler = null;
        }

        /// <summary>
        /// 清理事件
        /// </summary>
        public void Clear()
        {
            lock (m_Events)
            {
                m_Events.Clear();
            }
        }

        /// <summary>
        /// 获取事件处理函数的数量
        /// </summary>
        /// <param name="id">事件类型编号</param>
        /// <returns>事件处理函数的数量</returns>
        public int Count(int id)
        {
            if (m_EventHandlers.TryGetValue(id, out var range))
                return range.Count;
            return 0;
        }

        /// <summary>
        /// 检查事件处理函数是否存在
        /// </summary>
        public bool Check(int id, EventHandler<T> handler)
        {
            if (handler == null)
                throw new Exception("Event handler is invalid.");
            if (m_EventHandlers.TryGetValue(id, out var range))
            {
                return range.Contains(handler);
            }

            return false;
        }

        /// <summary>
        /// 订阅事件处理函数
        /// </summary>
        /// <param name="id">事件类型编号</param>
        /// <param name="handler">要订阅的事件处理函数</param>
        /// <exception cref="Exception"></exception>
        public void Subscribe(int id, EventHandler<T> handler)
        {
            if (handler == null)
                throw new Exception("Event handler is invalid.");
            if (!m_EventHandlers.ContainsKey(id))
            {
                m_EventHandlers.Add(id, new List<EventHandler<T>>() { handler });
            }
            //  mode.Default和mode.AllowNoHandler的情况(0, 1)
            else if ((m_EventPoolMode & EventPoolMode.AllowMultiHandler) != EventPoolMode.AllowMultiHandler)
                throw new Exception(string.Format("Event '{0}' not allow multi handler.", id));
            //  0, 1, 2的情况
            else if ((m_EventPoolMode & EventPoolMode.AllowDuplicateHandler) != EventPoolMode.AllowDuplicateHandler &&
                     Check(id, handler))
                throw new Exception(string.Format("Event '{0}' already has handler '{1}'.", id, handler.Method));
            else
                m_EventHandlers[id].Add(handler);
        }


        /// <summary>
        /// 取消订阅事件处理函数
        /// </summary>
        /// <param name="id"></param>
        /// <param name="handler"></param>
        /// <exception cref="Exception"></exception>
        public void Unsubscribe(int id, EventHandler<T> handler)
        {
            if (handler == null)
                throw new Exception("Event handler is invalid.");

            //  缓存节点数量大于0，表示当前正在遍历分发事件处理列表
            if (m_CachedNodes.Count > 0)
            {
                foreach (var cachedNode in m_CachedNodes)
                {
                    //  正在执行的节点（cachedNode.Value的第一个）等于当前要删除的元素，则不能直接删除
                    //  因为会丢失头结点
                    if (cachedNode.Value != null && cachedNode.Value.Value == handler)
                        m_TempNodes.Add(cachedNode.Key, cachedNode.Value.Next);
                }

                //  使用临时缓存的节点而不是直接修改的原因是为了避免在多线程环境有异常
                if (m_TempNodes.Count > 0)
                {
                    foreach (var cachedNode in m_TempNodes)
                    {
                        //  赋值，等于最后一个节点
                        m_CachedNodes[cachedNode.Key] = cachedNode.Value;
                    }

                    m_TempNodes.Clear();
                }
            }
            
            //  其他情况直接删除
            if (!m_EventHandlers[id].Remove(handler))
            {
                throw new Exception(string.Format("Event '{0}' not exists specified handler.", id));
            }
        }

        /// <summary>
        /// 设置默认事件处理函数
        /// </summary>
        /// <param name="handler"></param>
        public void SetDefaultHandler(EventHandler<T> handler)
        {
            m_DefaultHandler = handler;
        }

        /// <summary>
        /// 抛出事件，这个操作是线程安全的，即使不在主线程中抛出，也可保证主线程中回调事件处理函数，
        /// 但事件会在抛出后的下一帧分发。
        /// </summary>
        /// <param name="sender">事件源</param>
        /// <param name="e">事件参数</param>
        /// <exception cref="Exception"></exception>
        public void Fire(object sender, T e)
        {
            if (e == null)
            {
                throw new Exception("Event handler is invalid.");
            }

            var eventNode = Event.Create(sender, e);
            lock (m_Events)
                m_Events.Enqueue(eventNode);
        }

        /// <summary>
        /// 立即发送事件，如果不在主线程中调用，请勿使用此方法
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        /// <exception cref="Exception"></exception>
        public void FireNow(object sender, T e)
        {
            if (e == null)
                throw new Exception("Event handler is invalid.");
            HandlerEvent(sender, e);
        }
        
        /// <summary>
        /// 执行缓存的Event
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        /// <exception cref="Exception"></exception>
        private void HandlerEvent(object sender, T e)
        {
            //  是否有异常
            bool noHandlerException = false;
            if (m_EventHandlers.TryGetValue(e.Id, out var range))
            {
                //  这样有风险，handler（事件处理函数）可能会操作range
                //  即这个事件处理函数可能会订阅或取消订阅事件。
                foreach (var handler in range)
                {
                    handler.Invoke(sender, e);
                }

                m_CachedNodes.Remove(e);
            }else if (m_DefaultHandler != null)
                m_DefaultHandler(sender, e);
            //  事件处理函数不存在，但事件池没有允许没有处理函数模式，则有异常。
            else if ((m_EventPoolMode & EventPoolMode.AllowNoHandler) == 0)
                noHandlerException = true;
            ReferencePool.Release(e);
            //  有异常就抛出
            if(noHandlerException)
                throw new Exception(string.Format("Event '{0}' not allow multi handler.", e.Id));
        }

    }
}